package com.cybertron.lockdown.spider;

import com.cybertron.lockdown.spider.domain.Field;
import com.cybertron.lockdown.spider.domain.Parser;
import com.cybertron.lockdown.spider.domain.Site;
import com.cybertron.lockdown.spider.domain.Target;
import com.cybertron.lockdown.spider.util.ConnectionUtil;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * Created by xiezhouyan on 15-1-13.
 */
public class LockdownSpiderListener extends SpiderListener {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private final FelEngine fel = new FelEngineImpl();

    public LockdownSpiderListener(Site site) {
        super(site);
        fel.getContext().set("$StringUtils", StringUtils.class);
    }

    @Override
    public void pageProcess(Page page) {
        Html html = page.getHtml();
        Site site = getSite();
        List<Parser> sourceParsers = site.getSource().getParsers();
        if (sourceParsers == null || sourceParsers.size() == 0) {
            throw new NullPointerException("site is null or length =0");
        }
        for (Parser parser : sourceParsers) {
            Selectable selectable = html;
            if (StringUtils.isNotBlank(parser.getXpath())) {
                selectable = selectable.xpath(parser.getXpath());
            }
            if (StringUtils.isNotBlank(parser.getCss())) {
                selectable = selectable.css(parser.getCss());
            }
            if (StringUtils.isNotBlank(parser.getRegex())) {
                selectable = selectable.regex(parser.getRegex());
            }
            if (StringUtils.isNotBlank(parser.getExp())) {
                fel.getContext().set("$this", selectable);
                Object o = fel.eval(parser.getExp());
                try {
                    page.addTargetRequest(o.toString());
                } catch (Exception e) {
                    logger.error("exp error with " + parser.getExp() + " in " + page.getRequest().getUrl());
                } finally {
                    fel.getContext().set("$this", "");
                }
            } else {
                page.addTargetRequests(selectable == null ? html.all() : selectable.all());
            }
        }
        Target target = site.getTarget();
        if (!page.getUrl().regex(target.getRegex()).match()) {
            return;
        }
        List<Field> fields = site.getTarget().getFields();
        if (sourceParsers == null || sourceParsers.size() == 0) {
            throw new NullPointerException("site " + site.getDomain() + " process url " + page.getUrl() + " error by fields is null");
        }
        for (Field field : fields) {
            processField(field, page);
        }
    }

    private void processField(Field field, Page page) {
        Html html = page.getHtml();
        List<Parser> fieldParsers = field.getParsers();
        if (fieldParsers == null || fieldParsers.size() == 0) {
            page.putField(field.getName(), "");
            return;
        }
        StringBuilder sb = new StringBuilder();
        for (Parser parser : fieldParsers) {
            Selectable selectable = html;
            if (StringUtils.isNotBlank(parser.getXpath())) {
                selectable = selectable.xpath(parser.getXpath());
            }
            if (StringUtils.isNotBlank(parser.getCss())) {
                selectable = selectable.css(parser.getCss());
            }
            if (StringUtils.isNotBlank(parser.getRegex())) {
                selectable = selectable.regex(parser.getRegex());
            }
            if (StringUtils.isNotBlank(parser.getExp())) {
                fel.getContext().set("$this", selectable);
                Object o = fel.eval(parser.getExp());
                try {
                    sb.append(o.toString());
                } catch (Exception e) {
                    logger.error("exp error with " + parser.getExp() + " in " + page.getRequest().getUrl());
                } finally {
                    fel.getContext().set("$this", "");
                }
            } else {
                String result = StringUtils.join(selectable.all(), "");
                sb.append(result);
            }
        }
        page.putField(field.getName(), sb.toString());
    }


    @Override
    public void pipelineProcess(ResultItems items, Task task) {
        String url = items.getRequest().getUrl();
        String id = DigestUtils.md5Hex(items.getRequest().getUrl());
        String title = items.get("title");
        String keywords = items.get("keywords");
        String description = items.get("description");
        String price = items.get("price");
        String image = items.get("image");
        String summary = items.get("summary");
        String sql = "SELECT id FROM product  WHERE id='" + id + "'";
        DataSource dataSource = ConnectionUtil.getDataSource();
        QueryRunner runner = new QueryRunner(dataSource);
        try {
            String result = runner.query(sql, new ResultSetHandler<String>() {
                @Override
                public String handle(ResultSet rs) throws SQLException {
                    if (rs.next()) {
                        return rs.getString(1);
                    }
                    return null;
                }
            });
            if (StringUtils.isNotBlank(result)) {
                runner = new QueryRunner(dataSource);
                sql = "UPDATE product SET title=?,keywords=?,description=?,price=?,image=?,summary=?,updateTime=NOW() WHERE id=?";
                runner.update(sql, title, keywords, description, price, image, summary, id);
            } else {
                runner = new QueryRunner(dataSource);
                sql = "INSERT INTO product(id,url,title,keywords,description,price,image,summary,createTime,updateTime) VALUES(?,?,?,?,?,?,?,?,NOW(),NOW())";
                runner.update(sql, id, url, title, keywords, description, price, image, summary);
            }
        } catch (SQLException e) {
            logger.error("pipelie error", e);
        }
    }
}
